package com.xy.lang.domain;

import java.io.Serializable;
import java.util.Objects;
import java.util.function.Function;

/**
 *
 * @param <T>
 */
public class Result<T> implements Serializable {

    private static final long serialVersionUID = -1L;

    private static final String DEFAULT_SUB_CODE = null;
    private static final String DEFAULT_ERROR_MESSAGE = "服务内部错误，请联系系统管理人员！";
    private static final String DEFAULT_ERROR_MESSAGE_BAD_REQUEST = "无效的请求";
    private static final String DEFAULT_ERROR_MESSAGE_NOT_FOUND = "未查找到数据";
    private static final String DEFAULT_ERROR_MESSAGE_CONFLICT = "数据冲突";
    private static final String DEFAULT_ERROR_MESSAGE_ERROR_STATUS = "错误的状态";
    private static final String DEFAULT_ERROR_MESSAGE_ERROR_DATA = "错误的状态";
    private static final String DEFAULT_ERROR_MESSAGE_ERROR_PARAM = "错误的参数";

    /**
     * failure
     */
    public static final int FAILURE = 0;
    /**
     * success
     */
    public static final int SUCCESS = 1;

    /**
     * bad request
     */
    public static final String BAD_REQUEST = "bad-request";
    /**
     * unauthorized
     */
    public static final String UNAUTHORIZED = "unauthorized";
    /**
     * forbidden
     */
    public static final String FORBIDDEN = "forbidden";
    /**
     * not found
     */
    public static final String NOT_FOUND = "not-found";
    /**
     * request timeout
     */
    public static final String REQUEST_TIMEOUT = "request-timeout";
    /**
     * conflict
     */
    public static final String CONFLICT = "conflict";

    /**
     * internal server error
     */
    public static final String INTERNAL_SERVER_ERROR = "internal-server-error";
    /**
     * not implemented
     */
    public static final String NOT_IMPLEMENTED = "not-implemented";
    /**
     * service unavailable
     */
    public static final String SERVICE_UNAVAILABLE = "service-unavailable";
    /**
     * error rows affected
     */
    public static final String ERROR_ROWS_AFFECTED = "error-rows-affected";
    /**
     * error status
     */
    public static final String ERROR_STATUS = "error-status";
    /**
     * error data
     */
    public static final String ERROR_DATA = "error-data";
    /**
     * repetition consume
     */
    public static final String REPETITION_CONSUME = "repetition-consume";

    /**
     * code
     */
    private Integer code;
    /**
     * sub code
     */
    private String subCode;
    /**
     * message
     */
    private String msg;
    /**
     * data
     */
    private T data;

    /**
     *
     */
    public Result() {

    }

    /**
     *
     * @param code
     */
    public Result(Integer code) {
        this.code = code;
    }

    /**
     *
     * @param code
     * @param subCode
     */
    public Result(Integer code, String subCode) {
        this(code);
        this.subCode = subCode;
    }

    /**
     *
     * @param code
     * @param subCode
     * @param msg
     */
    public Result(Integer code, String subCode, String msg) {
        this(code, subCode);
        this.msg = msg;
    }

    /**
     *
     * @param code
     * @param subCode
     * @param msg
     * @param data
     */
    public Result(Integer code, String subCode, String msg, T data) {
        this(code, subCode, msg);
        this.data = data;
    }

    /**
     *
     *
     * @return
     */
    public boolean wasSuccess() {
        return Objects.equals(SUCCESS, code);
    }

    /**
     *
     *
     * @return
     */
    public boolean wasFailure() {
        return Objects.equals(FAILURE, code);
    }

    /**
     *
     * @return
     */
    public boolean wasBadRequest() {
        return Objects.equals(BAD_REQUEST, subCode);
    }

    /**
     *
     * @return
     */
    public boolean wasNotFound() {
        return Objects.equals(NOT_FOUND, subCode);
    }

    /**
     *
     * @param <TT>
     * @return
     */
    public <TT> Result<TT> transfer() {
        Result<TT> target = new Result<>();
        target.setCode(this.code);
        target.setSubCode(this.subCode);
        target.setMsg(this.msg);
        return target;
    }

    /**
     *
     * @param clazz
     * @param <TT>
     * @return
     */
    public <TT> Result<TT> transfer(Class<TT> clazz) {
        Result<TT> target = transfer();
        T result0;
        if(clazz != null && (result0 = this.data) != null && clazz.isInstance(result0)) {
            target.setData((TT)result0);
        }
        return target;
    }

    /**
     *
     * @param delegate
     * @param <TT>
     * @return
     */
    public <TT> Result<TT> transfer(Function<T, TT> delegate) {
        Result<TT> target = transfer();
        T result0;
        if(delegate != null && (result0 = this.data) != null) {
            target.setData(delegate.apply(result0));
        }
        return target;
    }

    /**
     *
     * @param <D>
     * @return
     */
    public static <D> Result<D> success() {
        return new Result<>(SUCCESS);
    }

    /**
     *
     * @param data
     * @param <D>
     * @return
     */
    public static <D> Result<D> success(D data) {
        Result<D> result = new Result<>(SUCCESS);
        result.setData(data);
        return result;
    }

    /**
     *
     * @param <D>
     * @return
     */
    public static <D> Result<D> fail() {
        return new Result<D>(FAILURE, DEFAULT_SUB_CODE, DEFAULT_ERROR_MESSAGE);
    }

    /**
     *
     * @param subCode
     * @param <D>
     * @return
     */
    public static <D> Result<D> fail(String subCode) {
        return new Result<D>(FAILURE,
                (subCode == null ? DEFAULT_SUB_CODE : subCode),
                DEFAULT_ERROR_MESSAGE);
    }

    /**
     *
     * @param subCode
     * @param msg
     * @param <D>
     * @return
     */
    public static <D> Result<D> fail(String subCode, String msg) {
        return new Result<D>(FAILURE,
                (subCode == null ? DEFAULT_SUB_CODE : subCode),
                (msg == null ? DEFAULT_ERROR_MESSAGE : msg));
    }

    /**
     *
     * @param subCode
     * @param msg
     * @param data
     * @param <D>
     * @return
     */
    public static <D> Result<D> fail(String subCode, String msg, D data) {
        return new Result<D>(FAILURE,
                (subCode == null ? DEFAULT_SUB_CODE : subCode),
                (msg == null ? DEFAULT_ERROR_MESSAGE : msg),
                data);
    }

    /**
     *
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofBadRequest() {
        return fail(BAD_REQUEST, DEFAULT_ERROR_MESSAGE_BAD_REQUEST);
    }

    public static <D> Result<D> ofBadParameter() {
        return fail(BAD_REQUEST, DEFAULT_ERROR_MESSAGE_ERROR_PARAM);
    }

    /**
     *
     * @param message
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofBadRequest(String message) {
        return fail(BAD_REQUEST, message);
    }

    /**
     *
     * @param message
     * @param request
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofBadRequest(String message, Object request) {
        return fail(BAD_REQUEST, (message == null ? DEFAULT_ERROR_MESSAGE_BAD_REQUEST : message) + ": " + request);
    }

    /**
     *
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofNotFound() {
        return fail(NOT_FOUND, DEFAULT_ERROR_MESSAGE_NOT_FOUND);
    }

    /**
     *
     * @param message
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofNotFound(String message) {
        return fail(NOT_FOUND, message);
    }

    /**
     *
     * @param message
     * @param object
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofNotFound(String message, Object object) {
        return fail(NOT_FOUND, (message == null ? DEFAULT_ERROR_MESSAGE_NOT_FOUND : message) + ": " + object);
    }

    /**
     *
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofConflict() {
        return fail(CONFLICT, DEFAULT_ERROR_MESSAGE_CONFLICT);
    }

    /**
     *
     * @param message
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofConflict(String message) {
        return fail(CONFLICT, message);
    }

    /**
     *
     * @param message
     * @param request
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofConflict(String message, Object request) {
        return fail(CONFLICT, (message == null ? DEFAULT_ERROR_MESSAGE_CONFLICT : message) + ": " + request);
    }

    /**
     *
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofErrorRowsAffected() {
        return fail(ERROR_ROWS_AFFECTED, DEFAULT_ERROR_MESSAGE);
    }

    /**
     *
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofErrorStatus() {
        return fail(ERROR_STATUS, DEFAULT_ERROR_MESSAGE_ERROR_STATUS);
    }

    /**
     *
     * @param message
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofErrorStatus(String message) {
        return fail(ERROR_STATUS, message);
    }

    /**
     *
     * @param message
     * @param object
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofErrorStatus(String message, Object object) {
        return fail(ERROR_STATUS, (message == null ? DEFAULT_ERROR_MESSAGE_ERROR_STATUS : message) + ": " + object);
    }

    /**
     *
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofErrorData() {
        return fail(ERROR_DATA, DEFAULT_ERROR_MESSAGE_ERROR_DATA);
    }

    /**
     *
     * @param message
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofErrorData(String message) {
        return fail(ERROR_DATA, message);
    }

    /**
     *
     * @param message
     * @param object
     * @param <D>
     * @return
     */
    public static <D> Result<D> ofErrorData(String message, Object object) {
        return fail(ERROR_DATA, (message == null ? DEFAULT_ERROR_MESSAGE_ERROR_DATA : message) + ": " + object);
    }

    /**
     *
     * @param source
     * @return
     */
    public static <D> Result<D> copy(Result<?> source) {
        return copy(source, (data) -> {
            if(data != null) {
                return (D)data;
            }
            return null;
        });
    }

    /**
     *
     * @param source
     * @param delegate
     * @param <D>
     * @return
     */
    public static <D> Result<D> copy(Result<?> source, Function<Object, D> delegate) {
        if(source == null) {
            Objects.requireNonNull(source, "The source is required.");
        }
        if(delegate == null) {
            Objects.requireNonNull(delegate, "The delegate is required.");
        }
        Result<D> target = new Result<>();
        target.setCode(source.getCode());
        target.setSubCode(source.getSubCode());
        target.setMsg(source.getMsg());
        target.setData(delegate.apply(source.getData()));
        return target;
    }

    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", subCode='" + subCode + '\'' +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getSubCode() {
        return subCode;
    }

    public void setSubCode(String subCode) {
        this.subCode = subCode;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }


    /// test

    public static void main(String[] args) {
        System.out.println(Result.success());
        System.out.println(Result.success("Hello World"));
        System.out.println(Result.fail());
        System.out.println(Result.fail("error"));
        System.out.println(Result.fail("error", "错误"));
        System.out.println(Result.fail("error", "错误", 789));
        System.out.println(Result.ofBadRequest("无效的输入"));
        System.out.println(Result.ofBadRequest(null, "123"));
        System.out.println(Result.ofBadRequest("无效的参数", "123"));
        System.out.println(Result.ofNotFound("系统不存在"));
        System.out.println(Result.ofNotFound(null, "123"));
        System.out.println(Result.ofNotFound("不存在的", "123"));
        Result<Integer> result1 = Result.success(123);
        Result<Integer> result2 = Result.copy(result1);
        Result<String> result3 = result1.transfer(e -> e.toString());
        System.out.println(result1 == result2);
        System.out.println(result2);
        System.out.println(result3);
    }

}
